var fso = WScript.CreateObject("Scripting.FileSystemObject");
var shell = WScript.CreateObject("WScript.Shell");

var dniDLURL = "http://dotnetinstaller.codeplex.com/releases/view/50143#DownloadId=140583";
var unzDLURL = "ftp://ftp.info-zip.org/pub/infozip/win32/unz600xn.exe";

/**
 * Get a file from the internet, and save it on the local machine.
 *
 * @param {String} srcURL the fully qualified URL to the file to download
 * @param {String} outFile the fully qualified path to save the download to
 */
function download( srcURL, outFile ) {
    var xhr = WScript.CreateObject("MSXML2.XMLHTTP.3.0");
    var binStream = WScript.CreateObject("ADODB.Stream");
    var parentFolder = fso.GetParentFolderName( outFile );


    WScript.Echo("Downloading " + outFile + "...");
    xhr.open("GET", srcURL, false);
    xhr.send();
    WScript.Echo("Complete!");

    binStream.Type = 1 /*adTypeBinary*/;
    binStream.Open();
    binStream.Write( xhr.responseBody );
    
    
    if (!fso.FolderExists(parentFolder)) {
        fso.CreateFolder(parentFolder);
    }
    binStream.SaveToFile( outFile, 2 /*adSaveCreateOverwrite*/ );
}

/**
 * Compute the full path to a location in the build folder
 *
 * @param {String} strRelPath a path relative to the build folder\
 * @return {String} a fully-qualified path
 */
function getBuildLocation( strRelPath ) {
    var scriptLocation = fso.BuildPath( fso.GetParentFolderName(WScript.ScriptFullName), "Build");

    return fso.BuildPath(scriptLocation, strRelPath);
}

/**
 * Compute the full path to a location in the setup folder
 *
 * @param {String} strRelPath a path relative the setup folder.
 * @return {String} a fully-qualified path
 */
function getSetupLocation( strRelPath ) {
    var scriptLocation = fso.BuildPath( fso.GetParentFolderName(WScript.ScriptFullName), "Setup");

    return fso.BuildPath(scriptLocation, strRelPath);
}

/**
 * Run a command line process and wait for it's completion.
 *
 * @param {String} cmd the command line to execute
 * @return the exit code of the executed process
 */
function execute( cmd, showErrors ) {
    WScript.Echo(cmd);
    var proc = shell.Exec(cmd);
    while (proc.Status == 0) {
        WScript.Echo(proc.StdOut.ReadLine());
    }

    if ( showErrors && !proc.StdErr.AtEndOfStream) {
        WScript.Echo("!!!!!!!!!!!! ERRORS !!!!!!!!!!!!");
        WScript.Echo(proc.StdErr.ReadAll());
    }

    return proc.ExitCode;
}


/**
 * Make sure that we are running under CScript.exe.... feedback messages
 * get annoying otherwise.  Do this by starting a new process if we are 
 * in the wrong one.  May never return.
 */
function verifyContext() {
    if ( "wscript.exe" == fso.GetFileName(WScript.FullName).toLowerCase() ) {
        shell.Run( "cscript /nologo \"" + WScript.ScriptFullName + "\"" );
        WScript.Quit();
    }
}

/**
 * This is a class to handle various archiving operations, along with
 * ensuring that the environment for such is set up
 *
 * @constructor
 */
function Unzip() {
    var _installDir = "";

    /**
     * Perform the details of making sure 7-zip is located on the system.
     */
    function _extractUnzip() {
        var archive = getBuildLocation("unz600xn.exe");
        download( unzDLURL, archive );

        var cmd = archive + " -d " + _installDir;  
        WScript.Echo("Extracting unzip to " +  _installDir + "...");
        execute( cmd );
    }

    // Determine whether to use the default or configured install location
    var _installDir = shell.Environment.Item("UNZIP_HOME");
    if ( 0 == _installDir.length) {
        _installDir = getBuildLocation("bin");
    }
    
    // Now set up the tool locations
    var _extractor = fso.BuildPath( _installDir, "unzip.exe");
    if ( !fso.FileExists(_extractor) ) {
        _extractUnzip();
    }

    /**
     * Uncompress the provided zip file
     *
     * @param {String} src full path to the zip archive to uncompress
     * @param {String} destDir optional full path to extract the archive to
     * @return the result code of the extration operation
     */
    this.extract = function( src, destDir ) {
        var cmd = _extractor + " " + src;
        if (destDir != undefined) {
            cmd += " -d " + destDir;
        }

        WScript.Echo("Extracting " + src + " to " + destDir + "...");
        return execute( cmd );
    };
}

/**
 * Encapsulate the operations and functionality of the dotNetInstaller.
 *
 * @constructor
 */
function DNI(unz) {
    var _unpackDir = getBuildLocation("dotNetInstaller 2.0");
    var _dlPackage = getBuildLocation("dotNetInstaller2.0.zip");
    var _unz = unz;
    var _installer = fso.BuildPath( _unpackDir, "Bin\\dotNetInstaller.exe" );
    var _linker = fso.BuildPath( _unpackDir, "Bin\\InstallerLinker.exe" );

    // Try to download and install if necessary
    if (!(fso.FileExists(_installer) && fso.FileExists(_linker))) {
        // None of the live download dlls work from a script.. so get it manually for now.
        //download( dniDLURL, _dlPackage ); 
        _unz.extract( _dlPackage, getBuildLocation("") );
    }

    /**
     * Output a self-contained bootstrap exe
     *
     * @param {String} configFile path to the file to be used to configure the output
     * @param {String} outFile path to the genereted installer
     * @param {Dictionary} options an associative array of options. Optional.  There is one option for each
     *  optional command line parameter of the linker.
     */
    this.link = function( configFile, outFile, options ) {
        var optOptions = [
            "Banner", "Splash", "EmbedFile", "EmbedFolder", 
            "EmbedHtml", "EmbedResoourceSize", "Icon", "Manifest",
            "ProcessorArchitecture", "Template" ];
        var optBoolOptions = [ "Embed", "Verbose", "Mslu" ];
        
        if (!options) options = {};
        if (!options["Template"]) options["Template"] = _installer;

        var cmd = "\"__linkerPath__\" \"/Configuration:__configPath__\"  \"/Output:__outPath__\" \"/AppPath:__appPath__\"".
            replace("__linkerPath__", _linker).replace("__configPath__", configFile).
            replace("__outPath__", outFile).replace("__appPath__",
                fso.GetParentFolderName(WScript.ScriptFullName));
        for (key in options) {
            for (test in optOptions) {
                if (key === optOptions[test]) {
                    cmd += " \"/__key__:__value__\"".replace("__key__", key).replace("__value__", options[key]);
                    break;
                }
            }
            for (test in optBoolOptions) {
                if (key === optBoolOptions[test]) {
                    cmd += " /__key____mod__".replace("__key__", key).replace("__mod__",
                        options[key] ? "+" : "-" );
                    break;
                }
            }
        }

        return execute(cmd, true);
    };
}

//if (__name__ == "__main__"):
   verifyContext();

   // Create the tools
   var unz = new Unzip();
   var dni = new DNI( unz );

   // Generate the installer
   dni.link(getSetupLocation("Configuration.xml"),
       getBuildLocation("VissemeeSetup.exe"), 
       {"Verbose": true,
        "Embed": true,
        "Banner": getSetupLocation("Banner.bmp")}); 
